﻿// Copyright (c) 2024 刻BITTER
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.


#pragma once

#include <cstddef>
#include <cstdint>

// 基本的运算工具
namespace lipid {

    /**
     * @brief 等比例映射
     *
     * 如果原数据超过了设置的原值域，那么映射后的数据也将超出新值域，映射函数本身不做限幅处理。
     *
     * @tparam V 数据类型
     * @param x 待映射的原始数据
     * @param in_min 原值域下界
     * @param in_max 原值域上界
     * @param out_min 新值域下界
     * @param out_max 新值域上界
     * @return V 映射后的新值
     */
    template <typename V>
    constexpr V value_map(V x, V in_min, V in_max, V out_min, V out_max) {
        return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    }


    template <typename V>
    constexpr V value_constrain(V x, V min, V max) {
        if (x < min) {
            return min;
        }
        else if (x > max) {
            return max;
        }
        else {
            return x;
        }
    }


    template <typename V>
    class ValueClamp {
       protected:
        V _limit_min = 0;
        V _limit_max = 0;
        V _mapping_min = 0;
        V _mapping_max = 0;

        V _k = 0;
        V _b = 0;

       public:
        ValueClamp(V limit_min, V limit_max) :
            _limit_min(limit_min), _limit_max(limit_max) {}

        ValueClamp(V limit_min, V limit_max, V mapping_min, V mapping_max) :
            _limit_min(limit_min),
            _limit_max(limit_max),
            _mapping_min(mapping_min),
            _mapping_max(mapping_max) {}

        ValueClamp() {}

        V get_limit_min() const {
            return _limit_min;
        }

        V get_limit_max() const {
            return _limit_max;
        }

        void set_limit(V min, V max) {
            if (min > max) {
                _limit_min = max;
                _limit_max = min;
            }
            else {
                _limit_max = max;
                _limit_min = min;
            }
        }

        V limit(V value) {
            return value_constrain(value, _limit_min, _limit_max);
        }

        V map(V value) {
            return value * _k + _b;
        }

        V get_mapping_min() const {
            return _mapping_min;
        }

        V get_mapping_max() const {
            return _mapping_max;
        }

        void set_mapping(V min, V max) {
            if (min > max) {
                _mapping_min = max;
                _mapping_max = min;
            }
            else {
                _mapping_min = min;
                _mapping_max = max;
            }

            _k = (_mapping_max - _mapping_min) / (_limit_max - _limit_min);
            _b = _mapping_min - _limit_min * _k;
        }
    };

}  // namespace lipid::count